package main

import (
	"fmt"
	"math"
)

// 开方
func Sqrt(x float64) float64 {
	z := 1.0

	// 精确度， 100.0 精确到小数点后两位 1000.0 精确到小数点后三位
	// 加 `.0` 是为了保持数据类型一致
	d := 1000.0

	// z,d := 1.0, 1000.0
	// z,d float64 = 1, 1000

	/*
	   for i := 0; i < 10; i++ {
	        z = z - (z*z-x)/(2*z)
	        fmt.Println(z)
	   }
	*/
	/*
	   // 来自百度百科-牛顿法(切线法-开方公式)
	   // 最后的 2 换成 3 即是开立方
	   X（n + 1） = Xn + (A / Xn − Xn)1 / 2 （n，n+1是下角标）
	   z = z + (x/z - z)/2
	*/

	// 无限循环
	for {
		old := z
		z = z - (z*z-x)/(2*z)
		// z = z + (x/z-z)/2

		// 当本次计算结果和上次结果 在小数点后两位相同时停止
		if uint(z*d) == uint(old*d) {
			fmt.Printf("Info: old= %v z=%v\n", old, z)
			return z
		}

		// fmt.Println(z)
	}

	return z
}

var v float64 = 2

/*
var v float64 = 5
5介于2的平方至3的平方之间，z 最好取2,3中间值 2.5
*/
func main() {
	// fmt.Println(Sqrt(v), math.Sqrt(v))
	fmt.Printf("Sqrt out: %v\n", Sqrt(v))
	fmt.Printf("math.Sqrt out: %v", math.Sqrt(v))
}

/*
练习：循环和函数
作为练习函数和循环的简单途径，用牛顿法实现开方函数。

在这个例子中，牛顿法是通过选择一个初始点 z 然后重复这一过程求 Sqrt(x) 的近似值：

z = z - (z*z - x) / (2*z)

为了做到这个，只需要重复计算 10 次，并且观察代入不同的x值（1，2，3，……）是如何逐步逼近结果的。 然后，修改循环条件，使得当值停止改变（或改变非常小）的时候退出循环。观察迭代次数是否变化。结果与 math.Sqrt 接近吗？

提示：定义并初始化一个浮点值，向其提供一个浮点语法或使用转换：

z := float64(1)
z := 1.0
*/
